package com.gui;

import com.gooddies.wiring.annotations.WiringComponent;
import com.peg.GrammarToken;
import com.peg.ParserConstructor;
import com.peg.PegRuleWalker;
import com.peg.rules.*;
import java.awt.Color;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

/**
 * @author sad
 */
@WiringComponent
public class PegHighlighter {
    //private Map<Class, Highlighter.HighlightPainter> highlighters = new HashMap<Class, Highlighter.HighlightPainter>();
    private Map<Class, Style> highlighterStyles = new HashMap<Class, Style>();
    private Map<GrammarToken.GrammarTokenType, Highlighter.HighlightPainter> painterStyles = new EnumMap<GrammarToken.GrammarTokenType, Highlighter.HighlightPainter>(GrammarToken.GrammarTokenType.class);
    private List<Object> usedPainters = new ArrayList<Object>();
    private Style defaultStyle;

    public PegHighlighter() {
        StyleContext context = new StyleContext();
        // build a style
        defaultStyle = context.addStyle("", null);
        Style actionStyle = context.addStyle("ActionRule", null);
        Style callRuleStyle = context.addStyle("CallRule", null);
        Style stringStyle = context.addStyle("StringRule", null);
        Style regexpStyle = context.addStyle("RegStyleRule", null);
        // set some style properties
        StyleConstants.setForeground(actionStyle, new Color(0, 180, 0));
        StyleConstants.setForeground(callRuleStyle, new Color(0, 0, 255));
        StyleConstants.setForeground(regexpStyle, new Color(137, 118, 203));

        StyleConstants.setBold(callRuleStyle, true);
        StyleConstants.setItalic(callRuleStyle, true);
        StyleConstants.setForeground(stringStyle, new Color(206, 123, 0));
        highlighterStyles.put(Action.class, actionStyle);
        highlighterStyles.put(RuleCallRule.class, callRuleStyle);
        highlighterStyles.put(Eoi.class, callRuleStyle);
        highlighterStyles.put(Any.class, callRuleStyle);
        highlighterStyles.put(StringRule.class, stringStyle);
        highlighterStyles.put(RegExpRule.class, regexpStyle);
        Highlighter.HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(new Color(230,230,230));
        painterStyles.put(GrammarToken.GrammarTokenType.RULE_START, painter);
    }

    private Style getStyle(Rule rule) {
        return highlighterStyles.get(rule.getClass());
    }

    private void clearHighlighters(JTextPane textPane) {
        StyledDocument sd = (StyledDocument) textPane.getDocument();
        for (Object painterTag : usedPainters) {
            textPane.getHighlighter().removeHighlight(painterTag);
        }

        sd.setCharacterAttributes(0, sd.getLength(), defaultStyle, true);
    }

    private void paintText(JTextPane textPane, Style style, int index, int length) {
        ((StyledDocument) textPane.getDocument()).setCharacterAttributes(index, length, style, false);
    }

    private void highlightOtherStuff(final JTextPane textPane, ParserConstructor constructor) {
        for (GrammarToken token : constructor.getOtherTokens()) {
            Highlighter.HighlightPainter painter = painterStyles.get(token.getGrammarTokenType());
            if (painter != null) {
                try {
                    usedPainters.add(textPane.getHighlighter().addHighlight(token.getStart(), token.getStart()+token.getLength(), painter));
                    
                } catch (BadLocationException ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    public void process(final JTextPane textPane, ParserConstructor constructor) {
        clearHighlighters(textPane);
        constructor.ruleWalker(new PegRuleWalker() {
            @Override
            public void ruleWalk(Rule rule) {
                if (rule.getStart() != -1 && rule.getLength() != -1) {
                    Style style = getStyle(rule);
                    if (style != null) {
                        paintText(textPane, style, rule.getStart(), rule.getLength());
                    }
                }
            }
        });
        highlightOtherStuff(textPane, constructor);
    }
}
